Skip to content

Zend: Fix method inheritance depending on the order of the interfaces - #23638

Open
lazerg wants to merge 1 commit into
php:masterfrom
lazerg:fix/issue-23630-interface-inherit-order
Open

lazerg wants to merge 1 commit into
php:masterfrom
lazerg:fix/issue-23630-interface-inherit-order

Conversation

@lazerg

@lazerg lazerg commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

interface C extends A, B {} fails while interface C extends B, A {} links, as soon as one parent carries a more specific signature than the other:

interface A { public function f(?string $name = null); }
interface B { public function f(?string $name = null, ?int $id = null); }

interface C extends A, B {} // Fatal error: Declaration of A::f() must be compatible with B::f()
interface D extends B, A {} // fine

When the child does not declare the method itself, the first parent's declaration is copied into its function table and every later parent is checked against that copy, so linking only works when the interface listed first happens to be the most specific one. Abstract classes are affected the same way.

The inherited method is now replaced when it is definitely not a valid override of a later parent while that parent is a valid override of it, which is the result the opposite order already produces. That is the approach nikic suggested in https://bugs.php.net/bug.php?id=76361. Only a definite error triggers it, so unresolved variance keeps the current behaviour, and signatures that genuinely conflict still fail in both orders.

Fixes #23630

@lazerg

lazerg commented Oct 17, 2026

Copy link
Copy Markdown
Contributor Author

Hey @Girgias, please take a look when you have time

@anun333

anun333 commented Oct 29, 2026

Copy link
Copy Markdown

Quoted below: written with Claude, an LLM, as CONTRIBUTING.md asks us to mark. The builds and test runs behind it are on my machine; the commands to repeat them are under the fold.

Tested on today's master (17cfa007d5d) with this applied, debug builds, each case in both parent orders where it applies:

master this PR
interface C extends A, B (the issue's A, B) fatal links
interface C extends B, A links links
a class implementing that C with B's f() fatal links
a class implementing it with only A's f() fatal fatal, as it should
abstract class K implements A, B / B, A fatal / links links / links
two incompatible parents (f(int), f(string)), both orders fatal fatal
GH-23630's constructors fatal links
Zend/tests and tests 6,448 passed 6,450 passed, 0 failed
How this was tested

Linux x86_64, gcc 13.3, debug builds of origin/master and of it with this PR's commit cherry-picked:

./buildconf --force
./configure --enable-debug --disable-cgi --disable-phpdbg --disable-fileinfo --enable-pcntl --enable-zend-test --enable-fpm --with-curl --enable-mbstring --disable-mbregex
make -j4

Each case compiled alone with sapi/cli/php -n -r '…', with interface A { public function f(?string $name = null); } and interface B { public function f(?string $name = null, ?int $id = null); }. Suites: TEST_PHP_EXECUTABLE=$PWD/sapi/cli/php sapi/cli/php run-tests.php -n -q -j4 Zend/tests tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Override construct function with optional argument should not produce fatal error

2 participants